package org.springframework.cloud.netflix.feign;

import feign.Client;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;

@Configuration
public class MockFeignClientAutoConfiguration {
    private static final String PREFIX = "mock.feign";
    private static final String API_BLUEPRINT = "api-blueprint";
    private static final String PROPERTY_API_BLUEPRINT = PREFIX + "." + API_BLUEPRINT;
    private static final String VALUE_PROPERTY_API_BLUEPRINT = "${" + PROPERTY_API_BLUEPRINT + ":}";
    private static final String FEIGN_CLIENT_FACTORY_BEAN_CLASS =
        FeignClientFactoryBean.class.getName();

    /**
     * Using default client instead of {@link org.springframework.cloud.netflix.feign.ribbon.LoadBalancerFeignClient}
     *
     * @return {@link Client.Default}
     */
    @Bean
    @Primary
    @ConditionalOnProperty(prefix = PREFIX, name = API_BLUEPRINT)
    public Client mockFeignClient() {
        return new Client.Default(null, null);
    }

    @Bean
    @ConditionalOnProperty(prefix = PREFIX, name = API_BLUEPRINT)
    public static MockFeignClientFactoryBeanPostProcessor mockFeignClientFactoryBeanPostProcessor(
        final Environment environment) {
        final String apiBlueprint = environment.resolvePlaceholders(VALUE_PROPERTY_API_BLUEPRINT);
        return new MockFeignClientFactoryBeanPostProcessor(apiBlueprint);
    }

    static class MockFeignClientFactoryBeanPostProcessor implements BeanDefinitionRegistryPostProcessor {
        private final String apiBlueprint;

        MockFeignClientFactoryBeanPostProcessor(String apiBlueprint) {
            this.apiBlueprint = apiBlueprint;
        }

        public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
            BeanDefinition definition;
            for (String bean : registry.getBeanDefinitionNames()) {
                definition = registry.getBeanDefinition(bean);
                if (FEIGN_CLIENT_FACTORY_BEAN_CLASS.equals(definition.getBeanClassName()) &&
                    null != apiBlueprint) {
                    definition.getPropertyValues().add("url", apiBlueprint);
                }
            }
        }

        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        }
    }
}
